﻿using System;
using System.Collections.Generic;
using System.Linq;
using TfsCommitMonitor.Interfaces;

namespace TfsCommitMonitor.Tfs
{
	internal class Project : IEquatable<Project>, ICloneable
	{
		private readonly ITfsBusiness _tfsBusiness;

		internal Project() : this(new TfsBusiness()) { }
		internal Project(ITfsBusiness tfsBusiness)
		{
			_tfsBusiness = tfsBusiness;
		}

		internal Uri CollectionUri { get; set; }
		
		private String _projectPath;
		internal String ProjectPath
		{
			get
			{
				return
					_projectPath;
			}
			set
			{
				_projectPath = value;
				if (!String.IsNullOrWhiteSpace(_projectPath) && !_projectPath.EndsWith("/"))
					_projectPath = String.Format("{0}/", _projectPath);
			}
		}

		internal Int32 RefreshTimeInMinutes { get; set; }
		internal String Error { get; private set; }
		
		private List<Changeset> _changesets = new List<Changeset>();
		internal List<Changeset> Changesets
		{
			get { return _changesets; }			
		}

		internal event EventHandler ChangesetsChanged;
		internal void OnChangesetsChanged()
		{
			if (ChangesetsChanged != null)
				ChangesetsChanged(this, EventArgs.Empty);
		}

		internal void MarkAsRead()
		{
			var changesetsToBeRead =
				_changesets
				.Where(changeset => !changeset.Read);

			changesetsToBeRead.MarkAsRead();

			OnChangesetsChanged();
		}

		private void StoreChangesets(IEnumerable<Changeset> changesets)
		{
			if (!_changesets.Any())
				_changesets.AddRange(changesets.Take(Configuration.Instance.MaxChangesToPersist));
			else
				_changesets =
					changesets
					.Union(_changesets)
					.Take(Configuration.Instance.MaxChangesToPersist)
					.ToList();

			OnChangesetsChanged();
		}

		internal Boolean PollForChanges()
		{
			Error = String.Empty;
			try
			{
				var changesets =
					_tfsBusiness.PollForChanges(CollectionUri, ProjectPath, LastChangesetId);

				StoreChangesets(changesets);

				return
					true;
			}
			catch (Exception ex)
			{
				DebugLogger.LogException(ex);
				Error = ex.Message;
				return false;
			}
		}

		internal Int32? LastChangesetId
		{
			get
			{
				return
					Changesets.Any()
					?
					(Int32?)Changesets.Max(changeset => changeset.ChangesetId)
					:
					null;
			}
		}

		internal Int32 UnreadCount
		{
			get { return Changesets.Where(changeset => !changeset.Read).Count(); }
		}

		public override Boolean Equals(Object obj)
		{
			return Equals(obj as Project);
		}

		public override Int32 GetHashCode()
		{
			return
				CollectionUri.GetHashCode()
				^
				ProjectPath.GetHashCode()
				^
				RefreshTimeInMinutes.GetHashCode()
				^
				Changesets.GetHashCode();
		}

		#region IEquatable<Project> Members

		public Boolean Equals(Project other)
		{
			return
				other != null
				&&
				CollectionUri.Equals(other.CollectionUri)
				&&
				ProjectPath == other.ProjectPath
				&&
				RefreshTimeInMinutes == other.RefreshTimeInMinutes
				&&
				Changesets.SequenceEqual(other.Changesets);
		}

		#endregion

		#region Clone Members
		internal Project Clone()
		{
			return 
				new Project
				{
					CollectionUri = new Uri(this.CollectionUri.ToString()),
					ProjectPath = this.ProjectPath,
					RefreshTimeInMinutes = this.RefreshTimeInMinutes,
					_changesets = this.Changesets.Select(changeset => changeset.Clone()).ToList(),
				};
		}

		Object ICloneable.Clone()
		{
			return Clone();
		}
		#endregion

	}

	internal static class ProjectHelper
	{
		internal static Int32 GetUnreadCount(this IEnumerable<Project> projects)
		{
			return
				projects.Sum(project => project.UnreadCount);
		}
	}
}
